﻿using System;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Runtime.Serialization;
using SW2A311_Lib;



namespace Client
{
    /// <summary>
    /// Håndtere en forbinelse til en server.
    /// </summary>
    class Network
    {
        #region Instansvariabler
        ObjectToByteArray serializer = new ObjectToByteArray();
        NetworkStream stream; //data stream til at sende og modtage data fra netværket
        EventDrivenShare EDS; //Event til at pushe data til GUI-tråden
        private MainWindow GUI;
        byte[] bytes;
        #endregion

        #region Constructors
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="EDSIn">EventDrivenShare, der håndtere in-/out-put</param>
        /// <param name="GuiIn">MainWindow der bruges til at prompte forbindelses status. Forventes at en delegate der hedder DelegateConnectStatus til at modtage kaldet</param>
        public Network(EventDrivenShare EDSIn, MainWindow GuiIn)
        {
            EDS = EDSIn;
            GUI = GuiIn;
        }
        #endregion

        #region Methods
        /// <summary>
        /// Prøver og forbinde til server med det specificeret information
        /// </summary>
        /// <param name="ip">ipaddresse der skal forbindes til</param>
        /// <param name="port">port på server der skal forbindes til</param>
        /// <param name="user">User som bruger at forbinde</param>
        /// <returns>returns true hvis forbindelsen blev oprettet succefuldt, ellers false</returns>
        public bool TryConnect(IPAddress ip, int port, User user)
        {
            try
            {
                TcpClient client = new TcpClient();

                //prøver at forbinde til en host på ovennævnte detaljer
                client.Connect(ip, port);

                //Henter streamet associerede med client
                stream = client.GetStream();

                return true;
            }
            catch (Exception e)
            {
                EDS.ChatMessage = e.Message;
                return false;
            }
        }

        /// <summary>
        /// Diconnecter fra server
        /// </summary>
        public void Disconnect()
        {
            if (stream != null)
                stream.Close();
            stream = null;
        }

        /// <summary>
        /// Start punkt for tråd. Modtager data fra den forbundne forbindelse.
        /// Tråden stopper hvis der sker en IOException
        /// </summary>
        public void ClientRecv()
        {
            bool run = true;

            while (run)
            {
                bytes = new byte[4096];
                stream.Flush(); //tømmer streamets buffer for at sørge for at data fra en gammel sending ikke corrupter en ny sending
                //dette kan ske hvis den "nye" sending ikke udfylder hele bufferen

                if (stream != null)

                    try
                    {
                        int i = 0;
                        if (!bytes.IsReadOnly)
                            i = stream.Read(bytes, 0, bytes.Length); //læser data fra stream ind i bytes

                        try
                        {
                            if (i != 0)
                                DistributedData(bytes); //pusher en data til GUI'en
                            else
                                EDS.ChatMessage = "You received unexpected data";
                        }
                        catch (SerializationException)
                        {
                            EDS.ChatMessage = "There were an error reading from the network stream. If you are missing a project please try and reconnect!";
                        }
                    }
                    catch (IOException)
                    {
                        EDS.ChatMessage = "There were an error reading from network stream";
                        run = false;
                        if (stream != null)
                            stream.Close();
                    }
            }

        }

        /// <summary>
        /// Reagere på et event.
        /// Sender data til forbundne server.
        /// </summary>
        /// <param name="sender">Eventets oprindelse</param>
        /// <param name="e">EventDrivenShare.EncapsulationArgs information om eventet. Det er det der bliver sendt</param>
        public void clientSend(object sender, EventDrivenShare.EncapsulationArgs e)
        {
            byte[] dataToSend = serializer.SerializeObject(e.Encapsulation);
            stream.Write(dataToSend, 0, dataToSend.Length); //skriver til stream.
        }

        /// <summary>
        /// Videre sender via den givne EventDrivenShare
        /// </summary>
        /// <param name="byteArray">Data der skal håndteres. Forventes at være en EncapsulationClass, hvis ikke bliver der kastet en SerializationException.</param>
        private void DistributedData(byte[] byteArray)
        {
            EncapsulationClass data;
            byte[] dataToSend = serializer.SerializeObject(new EncapsulationClass(new ChatMessageEncapsulation("")));
            try
            {
                data = serializer.DeSerializeObject<EncapsulationClass>(byteArray); //deserializes byteArray til encapsulationClass
            }
            catch (SerializationException)
            {
                throw;
            }

            stream.Flush();
            switch (data.TypeCode)
            {
                case ObjectType.Project:
                    ProjectData project = serializer.DeSerializeObject<ProjectData>(data.SentObject);
                    //Da severen ved authetication processen forventer et svar mellem hvert project sendes en tom chatbesked
                    stream.Write(dataToSend, 0, dataToSend.Length);
                    EDS.Project = project;
                    break;
                case ObjectType.Chat:
                    ChatMessageEncapsulation chatMsg = serializer.DeSerializeObject<ChatMessageEncapsulation>(data.SentObject);
                    if (!chatMsg.UserName.Equals(""))
                        EDS.ChatMessage = chatMsg.UserName + ": " + chatMsg.ChatMessageContent;
                    break;
                case ObjectType.LogInUser:
                    User user = serializer.DeSerializeObject<User>(data.SentObject);
                    GUI.Dispatcher.BeginInvoke(MainWindow.DelegateConnectStatus, user);
                    //Da severen ved authetication processen forventer et svar mellem hvert project sendes en tom chatbesked
                    stream.Write(dataToSend, 0, dataToSend.Length);
                    break;
                default: break;
            }

        }
        #endregion
    }

}